Skip to content

fix(wallet): stop giving Veil names away for free - #1329

Merged
Miracle656 merged 1 commit into
mainfrom
fix/names-api-closed
Nov 8, 2026
Merged

Miracle656 merged 1 commit into
mainfrom
fix/names-api-closed

Conversation

@Miracle656

Copy link
Copy Markdown
Owner

POST /api/names/claim is live in production, open to every origin, and
grants a name on the strength of a paymentTxHash that is never checked
against the ledger.

The registry's default verifiers are stubs that accept:

paymentVerifier   ?? (async (txHash) => Boolean(txHash && txHash.trim().length > 0))
signatureVerifier ?? (async () => true)

and globalNamesRegistry = new NamesRegistry() passes no options — so those
stubs are the production policy. Any caller can take any unreserved name by
sending a one-character paymentTxHash, bound to whatever passkeyPublicKey
they supply. The signature is only checked if (signature), so omitting it
skips the check rather than failing it, and nothing proves possession of the
passkey the name is bound to.

The route could not have honoured a real payment either: the registry is a set
of Maps behind a module singleton, so each serverless instance has its own,
instances are recycled, and a claim is invisible to the next request and gone
after a deploy.

Verified deployed with a non-mutating OPTIONS (204, Allow-Origin: *) and a
GET /api/names/available?name=adebayo returning available: true. I did not
POST to production to demonstrate the grant.

Changes

  • The verifier defaults refuse. A stub standing in for money has to fail
    closed, or the day something is wired to it is the day names become free.
    The unit tests that want permissive behaviour now pass their own verifiers at
    the construction site and say why.
  • Both routes return 503, via lib/namesApiGate.ts. Deliberately not an
    env flag — a flag is a switch somebody eventually flips, and what is behind
    it hands out names. GET /api/names/available is shut for its own reason:
    it answered from the always-empty registry, so every name returned
    available: true with Cache-Control: public, max-age=10 inviting clients
    to cache it.
  • Route tests assert the gate. They previously asserted 201 for
    paymentTxHash: 'tx_valid_jupiter' — the hole encoded as intended
    behaviour. Reserved/taken/charset coverage already lives in
    lib/__tests__/namesRegistry.test.ts, so nothing unique left coverage.

Not fixed, noted in place

The optional-signature gap. The on-chain registry (contracts/veil-names,
#1328) requires buyer auth for register, so the ledger proves possession;
hardening a module that registry retires is wasted work.

Scope

No caller anywhere in the repo reaches these routes — only their own tests —
so nothing regresses. Targets main because that is what the three Vercel
projects deploy from, so the hole stays open in production until this lands.

  • npx tsc --noEmit — clean
  • wallet suite — 100 suites, 1563 passed, 3 skipped

https://claude.ai/code/session_01USgemLt4Rnz4SGB1Srf3GB

`POST /api/names/claim` is deployed, open to every origin, and granted a name
on the strength of a `paymentTxHash` that was never checked against the
ledger. The registry's default verifiers were stubs that accepted:

    paymentVerifier   ?? (async (txHash) => Boolean(txHash && txHash.trim().length > 0))
    signatureVerifier ?? (async () => true)

and `globalNamesRegistry` is constructed with no options, so those stubs were
the production policy. Any caller could take any unreserved name by sending a
one-character hash, bound to whatever `passkeyPublicKey` they chose. The
signature was only checked `if (signature)`, so omitting it skipped the check
rather than failing it — nothing proved possession of the passkey the name was
being bound to.

The route could not have honoured a real payment either. The registry is a set
of `Map`s behind a module singleton: every serverless instance gets its own and
instances are recycled, so a claim is invisible to the next request and gone
after a deploy.

Three changes:

- **The verifier defaults now refuse.** A stub standing in for money has to
  fail closed, or the day something is wired to it is the day names become
  free. Callers that want permissive behaviour — the unit tests covering
  charset, reserved names and claim concurrency — now pass their own verifiers
  and say so at the construction site.
- **Both routes return 503**, via `lib/namesApiGate.ts`. Deliberately not an
  env flag: a flag is a switch somebody eventually flips, and what is behind it
  hands out names. `GET /api/names/available` is shut for its own reason — it
  answered from the always-empty registry, so every name came back
  `available: true`, with `Cache-Control: public, max-age=10` inviting clients
  to cache that.
- **The route tests assert the gate.** They previously asserted a 201 for
  `paymentTxHash: 'tx_valid_jupiter'`, which encoded the hole as intended
  behaviour. Reserved/taken/charset coverage already lives in
  `lib/__tests__/namesRegistry.test.ts`, so nothing unique left coverage.

The real registry is `contracts/veil-names`, where `register` requires `buyer`
auth so the ledger proves possession, and a name is rented by the year in
length bands rather than sold once for a flat 5 USDC. `namesApiGate.ts`
records what lifting the gate requires.

Not fixed, and noted in place instead: the optional-signature gap above.
Hardening a module that the on-chain registry retires is wasted work.

No caller anywhere in the repo reaches these routes — only their own tests —
so nothing regresses.

`npx tsc --noEmit` clean; wallet suite 100 suites / 1563 passed.

Claude-Session: https://claude.ai/code/session_01USgemLt4Rnz4SGB1Srf3GB
@vercel

vercel Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
veil Ready Ready Preview Nov 8, 2026 8:48am UTC
veil-2ap8 Ready Ready Preview Nov 8, 2026 8:48am UTC
veil-ezry Ready Ready Preview Nov 8, 2026 8:48am UTC

This branch was successfully deployed

3 active deployments
Preview – veil-ezry — 25fc579c Deployed Nov 8, 2026 by vercel[bot]
Preview – veil-2ap8 — 25fc579c Deployed Nov 8, 2026 by vercel[bot]
Preview – veil — 25fc579c Deployed Nov 8, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant